package fen_xiang

import (
	"device-modbus/core"
	"device-modbus/internal/biz/model"
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/common/hlog"
)

// FenXiangSensorProtocol 风向传感器协议实现（必须实现 core.SensorProtocol 接口）
type FenXiangSensorProtocol struct{}

// WindDirectionSensorData 解析后的风向传感器数据
type WindDirectionSensorData struct {
	DeviceAddr    byte    // 设备地址（1-254，默认0x01）
	FuncCode      byte    // 功能码（读数据：0x03；写参数：0x06）
	ValidBytes    byte    // 应答帧有效数据字节数
	SensorType    string  // 传感器类型（8方位/16方位/360°）
	Level8        uint16  // 8方位档位（0-7档，仅8方位型有效）
	Angle8        uint16  // 8方位角度（0-315°，仅8方位型有效）
	Angle360Full  float64 // 360°完整角度（0-359.9°，8/16/360°型均有效）
	Level16       uint16  // 16方位档位（0-15档，仅16方位型有效）
	Angle360Int   uint16  // 360°整数角度（0-359°，仅360°型有效）
	BaudRate      uint16  // 设备波特率（对应值：0=2400,1=4800<默认>,2=9600...7=1200）
	NewDeviceAddr uint16  // 新设备地址（仅修改地址时使用，1-254）
	DirectionName string  // 风向名称（如“北风”“东风”，自动匹配档位/角度）
}

var _ core.ProtocolCreator = NewProtocol // 编译时检查：若不匹配会直接报错

// NewProtocol 创建新的风向传感器协议实例
func NewProtocol() core.SensorProtocol {
	return &FenXiangSensorProtocol{}
}

// GetType 返回传感器类型
func (p *FenXiangSensorProtocol) GetType() string {
	return "modbus"
}

// GetModel 返回传感器型号
func (p *FenXiangSensorProtocol) GetModel() string {
	return "fen_xiang"
}

// GetProtocolName 返回协议唯一标识
func (p *FenXiangSensorProtocol) GetProtocolName() string {
	return "风向"
}

// BuildRequest 构建风向传感器请求帧（支持读风向、改地址、改波特率）
func (p *FenXiangSensorProtocol) BuildRequest(deviceAddr, funcCode byte, startAddr, dataLength uint16) ([]byte, error) {
	// 基础参数合法性校验（基于手册定义）
	if deviceAddr < 0x01 || deviceAddr > 0xFE {
		return nil, errors.New("设备地址非法，需在1-254范围内")
	}
	if funcCode != 0x03 && funcCode != 0x06 {
		return nil, errors.New("仅支持功能码0x03（读寄存器）和0x06（写寄存器）")
	}
	// 校验寄存器地址（仅支持手册中定义的关键寄存器）
	validStartAddrs := map[uint16]bool{0x0000: true, 0x07D0: true, 0x07D1: true}
	if !validStartAddrs[startAddr] {
		return nil, fmt.Errorf("起始寄存器地址非法，仅支持0x0000(风向数据)、0x07D0(设备地址)、0x07D1(波特率)")
	}
	// 读风向时固定读取2个寄存器（覆盖不同类型传感器的核心数据）
	if funcCode == 0x03 && startAddr == 0x0000 && dataLength != 0x0002 {
		return nil, errors.New("读风向数据时，寄存器长度需设为0x0002（读取2个核心寄存器）")
	}

	// 打印请求帧日志
	hlog.Debugf("构建风向传感器请求帧: 设备地址=0x%02X, 功能码=0x%02X, 起始寄存器=0x%04X, 数据长度/值=0x%04X",
		deviceAddr, funcCode, startAddr, dataLength)

	// 构建帧主体（6字节：地址码+功能码+起始地址(2字节)+数据长度/值(2字节)）
	frame := make([]byte, 6)
	frame[0] = deviceAddr
	frame[1] = funcCode
	binary.BigEndian.PutUint16(frame[2:4], startAddr)  // 寄存器地址高位在前
	binary.BigEndian.PutUint16(frame[4:6], dataLength) // 读：寄存器数量；写：参数值

	// 计算并添加CRC16校验码（遵循手册“低位在前、高位在后”规则）
	crc := calculateWindDirCRC(frame)
	frame = append(frame, byte(crc&0xFF))    // CRC低字节
	frame = append(frame, byte(crc>>8&0xFF)) // CRC高字节

	return frame, nil
}

// ParseResponse 解析风向传感器应答帧（自动适配8/16/360°型传感器）
func (p *FenXiangSensorProtocol) ParseResponse(resp []byte) (*model.SensorData, error) {
	// 1. 基础长度校验（读风向应答9字节，写参数应答8字节，最小长度8字节）
	if len(resp) < 8 {
		return nil, errors.New("应答帧长度不足，最小需8字节")
	}

	// 2. 提取帧头核心信息
	deviceAddr := resp[0]
	funcCode := resp[1]

	// 3. 设备错误响应检查（功能码最高位为1表示错误）
	if (funcCode & 0x80) != 0 {
		return nil, fmt.Errorf("设备返回错误，错误功能码=0x%02X", funcCode)
	}

	// 4. CRC校验（校验范围：除最后2字节CRC外的所有数据）
	dataPart := resp[:len(resp)-2]
	receivedCRC := binary.LittleEndian.Uint16(resp[len(resp)-2:]) // 接收的CRC（低位在前）
	calculatedCRC := calculateWindDirCRC(dataPart)
	if receivedCRC != calculatedCRC {
		return nil, fmt.Errorf("CRC校验失败，接收CRC=0x%04X，计算CRC=0x%04X", receivedCRC, calculatedCRC)
	}

	result := &WindDirectionSensorData{
		DeviceAddr: deviceAddr,
		FuncCode:   funcCode,
		ValidBytes: resp[2],
	}

	// 有效字节数校验（读2个寄存器需返回4字节数据）
	if result.ValidBytes != 0x04 {
		return nil, fmt.Errorf("读风向应答有效字节数异常，期望0x04，实际0x%02X", result.ValidBytes)
	}

	// 提取2个核心寄存器数据（0000H和0001H）
	reg0Data := binary.BigEndian.Uint16(resp[3:5]) // 0000H寄存器数据
	reg1Data := binary.BigEndian.Uint16(resp[5:7]) // 0001H寄存器数据

	// 自动识别传感器类型并解析数据（基于手册寄存器定义）
	switch {
	// 8方位型：reg0=0-7档，reg1=0-315°，额外通过0002H寄存器获取359.9°（此处简化用reg1推导）
	case reg0Data <= 7 && reg1Data <= 315 && reg1Data%45 == 0:
		result.SensorType = "8方位"
		result.Level8 = reg0Data
		result.Angle8 = reg1Data
		result.Angle360Full = float64(reg1Data) // 8方位角度对应固定值（0/45/90...315°）
		result.DirectionName = get8DirName(reg0Data)
		hlog.Debugf("8方位传感器解析结果：档位=%d，角度=%.1f°，风向=%s",
			result.Level8, result.Angle360Full, result.DirectionName)

	// 16方位型：reg0=0-359.9°，reg1=0-15档
	case reg0Data <= 3599 && reg1Data <= 15:
		result.SensorType = "16方位"
		result.Angle360Full = float64(reg0Data) / 10 // 0000H数据为角度×10（如3599→359.9°）
		result.Level16 = reg1Data
		result.DirectionName = get16DirName(reg1Data)
		hlog.Debugf("16方位传感器解析结果：档位=%d，角度=%.1f°，风向=%s",
			result.Level16, result.Angle360Full, result.DirectionName)

	// 360°型：reg0=0-3599（角度×10），reg1=0-359°（整数角度）
	case reg0Data <= 3599 && reg1Data <= 359:
		result.SensorType = "360°"
		result.Angle360Full = float64(reg0Data) / 10 // 还原为保留1位小数的角度
		result.Angle360Int = reg1Data
		result.DirectionName = get360DirName(result.Angle360Full)
		hlog.Debugf("360°传感器解析结果：整数角度=%d°，完整角度=%.1f°，风向=%s",
			result.Angle360Int, result.Angle360Full, result.DirectionName)

	default:
		return nil, fmt.Errorf("无法识别传感器类型，寄存器数据：0000H=0x%04X，0001H=0x%04X", reg0Data, reg1Data)
	}

	return &model.SensorData{
		SensorID:  fmt.Sprintf("%02X", deviceAddr),
		Type:      p.GetType(),
		Model:     p.GetModel(),
		Timestamp: time.Now().UnixMilli(),
		RawData:   resp,
		Values: map[string]string{
			"sensorType":    result.SensorType,
			"level8":        fmt.Sprintf("%d", result.Level8),
			"angle8":        fmt.Sprintf("%d", result.Angle8),
			"angle360Full":  fmt.Sprintf("%.1f", result.Angle360Full),
			"directionName": result.DirectionName,
		},
		Protocol: p.GetProtocolName(),
	}, nil
}

// calculateWindDirCRC 计算ModBus-RTU CRC16校验码（多项式0xA001，符合手册错误校验规则）
func calculateWindDirCRC(data []byte) uint16 {
	crc := uint16(0xFFFF) // 初始值
	for _, b := range data {
		crc ^= uint16(b)
		for i := 0; i < 8; i++ {
			if (crc & 0x0001) != 0 { // 最低位为1时，右移后异或多项式
				crc >>= 1
				crc ^= 0xA001
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}

// get8DirName 根据8方位档位获取风向名称（对应手册8方位换算表）
func get8DirName(level uint16) string {
	dirMap := map[uint16]string{
		0: "北风", 1: "东北风", 2: "东风", 3: "东南风",
		4: "南风", 5: "西南风", 6: "西风", 7: "西北风",
	}
	return dirMap[level]
}

// get16DirName 根据16方位档位获取风向名称（对应手册16方位换算表）
func get16DirName(level uint16) string {
	dirMap := map[uint16]string{
		0: "北风", 1: "北东北风", 2: "东北风", 3: "东东北风",
		4: "东风", 5: "东东南风", 6: "东南风", 7: "南东南风",
		8: "南风", 9: "南西南风", 10: "西南风", 11: "西西南风",
		12: "西风", 13: "西西北风", 14: "西北风", 15: "北西北风",
	}
	return dirMap[level]
}

// get360DirName 根据360°角度获取风向名称（适配8方位逻辑，角度对应档位）
func get360DirName(angle float64) string {
	switch {
	case angle >= 348.75 || angle < 11.25:
		return "北风"
	case angle >= 11.25 && angle < 33.75:
		return "北东北风"
	case angle >= 33.75 && angle < 56.25:
		return "东北风"
	case angle >= 56.25 && angle < 78.75:
		return "东东北风"
	case angle >= 78.75 && angle < 101.25:
		return "东风"
	case angle >= 101.25 && angle < 123.75:
		return "东东南风"
	case angle >= 123.75 && angle < 146.25:
		return "东南风"
	case angle >= 146.25 && angle < 168.75:
		return "南东南风"
	case angle >= 168.75 && angle < 191.25:
		return "南风"
	case angle >= 191.25 && angle < 213.75:
		return "南西南风"
	case angle >= 213.75 && angle < 236.25:
		return "西南风"
	case angle >= 236.25 && angle < 258.75:
		return "西西南风"
	case angle >= 258.75 && angle < 281.25:
		return "西风"
	case angle >= 281.25 && angle < 303.75:
		return "西西北风"
	case angle >= 303.75 && angle < 326.25:
		return "西北风"
	case angle >= 326.25 && angle < 348.75:
		return "北西北风"
	default:
		return "未知风向"
	}
}
